#ifndef _YL_LOG_
#define _YL_LOG_

#include "ylEssentials.h"
#include "ylMemoryStream.h"
#include "ylCriticalSection.h"
#include <hash_map>

struct addrinfo;

typedef void *HANDLE;

namespace yoctolog
{
	struct MessageTemplateInfo_s
	{
		std::string		strParameterFormat;
		unsigned char	ucMsgClass;
	};

	class Log
	{
	public:
		YL_API void Initialize();
		YL_API void Add(unsigned int uiID, ...);
		YL_API unsigned int CreateID(const char *szTag, const char *szMessage, const unsigned char ucMsgClass);
		YL_API void CreateMessageClass(unsigned char ucID, const char *szName, const char *szTextColor, const char *szBackgroundColor, bool bBold, bool bCallstackEnabled);

		YL_API void DoAsynchronousUpdate();
		YL_API void WaitForConnection();

		inline bool IsInitialized() const { return m_bInitialized; }
		inline bool IsQuitting() const { return m_bQuitting; }

		static Log &Instance() { if( !ms_pInstance ) ms_pInstance = new Log(); return *ms_pInstance; }

	private:
		YL_API Log();
		YL_API ~Log();

		YL_API static Log *							ms_pInstance;

		void ConnectToRemoteLogger();
		void SendData();
		void ResetStream();
		void SendProcessInfo();
		
		int											m_hSocket;
		int											m_hListeningSocket;
		bool										m_bHasRemoteConnection;
		addrinfo *									m_pAddrInfo;
		addrinfo *									m_pListeningAddrInfo;
		unsigned int								m_uiIDCounter;
		std::hash_map<unsigned int, MessageTemplateInfo_s>		m_mapTemplateInfo;
		HANDLE										m_hThread;
		HANDLE										m_hListenerThread;
		
		CriticalSection								m_csData;
		CriticalSection								m_csInterface;

		MemoryStream								m_xMemoryStream;
		unsigned int								m_uiSleepInterval;
		std::hash_map<unsigned char, bool>			m_mapClassCallstackEnabled;
		bool										m_bInitialized;
		bool										m_bQuitting;
	};


	enum LogMsgType_e
	{
		LOG_MSG_TEXT,
		LOG_MSG_CREATE_ID,
		LOG_MSG_PROCESS_INFO,
		LOG_MSG_CREATE_CLASS,
	};


	struct ILogNetMsg_s
	{
		YL_API virtual void Serialize(MemoryStream &xMemoryStream) = 0;
		YL_API virtual void Deserialize(const MemoryStream &xMemoryStream) = 0;

		unsigned char			ucMsgType;
	};

	struct LogNetMsg_s: public ILogNetMsg_s
	{
		LogNetMsg_s() { ucMsgType = LOG_MSG_TEXT; }

		YL_API void Serialize(MemoryStream &xMemoryStream);
		YL_API void Deserialize(const MemoryStream &xMemoryStream);

		unsigned int			uiID;
	};

	struct LogCreateIDMsg_s: public ILogNetMsg_s
	{
		LogCreateIDMsg_s() { ucMsgType = LOG_MSG_CREATE_ID; }

		YL_API void Serialize(MemoryStream &xMemoryStream);
		YL_API void Deserialize(const MemoryStream &xMemoryStream);

		unsigned int			uiID;
		unsigned char			ucMsgClass;
		std::string				strText;
		std::string				strTag;
	};

	struct LogCreateMsgClass_s: public ILogNetMsg_s
	{
		LogCreateMsgClass_s() { ucMsgType = LOG_MSG_CREATE_CLASS; }

		YL_API void Serialize(MemoryStream &xMemoryStream);
		YL_API void Deserialize(const MemoryStream &xMemoryStream);

		unsigned char			ucID;
		std::string				strName;
		std::string				strTextColor;
		std::string				strBackgroundColor;
		bool					bBold;
	};

	struct LogProcessInfo_s: public ILogNetMsg_s
	{
		LogProcessInfo_s() { ucMsgType = LOG_MSG_PROCESS_INFO; }

		YL_API void Serialize(MemoryStream &xMemoryStream);
		YL_API void Deserialize(const MemoryStream &xMemoryStream);

		unsigned int			hProcess;
		std::string				strComputerName;
	};

}

// NOTE: In the macro below, the "do while" is required in order for the macro to be usable in certain situations.
// See http://c-faq.com/cpp/multistmt.html.
#define YOCTOLOG(eMessageType, strTag, strMsg, ...) do { \
if( yoctolog::Log::Instance().IsInitialized() ) { \
	static unsigned int uiYoctoLogMessageID = 0; \
	if( uiYoctoLogMessageID == 0 ) \
{ \
	uiYoctoLogMessageID = yoctolog::Log::Instance().CreateID(strTag, strMsg, eMessageType); \
} \
	yoctolog::Log::Instance().Add(uiYoctoLogMessageID, __VA_ARGS__); \
} \
} while(0)


#endif